import {EventEmitter} from 'events'//(http://nodejs.cn/api/)Node.js文档
import { io } from "socket.io-client";//(https://socket.io/docs/v4/client-api/)socket.ioClientAPI
import * as mediaSoupClient from "mediasoup-client";
//(https://mediasoup.org/documentation/v3/mediasoup-client/api/)mediaSoup-client-v3API

/*自定义客户端*/
class RoomClient extends EventEmitter{
  /*构造函数*/
  constructor({ roomId,userName }){
    super();
    this._closed = false;//Closed flag.
    this._roomId = roomId;//Room id.
    this._userName = userName;//userName.
    this._device = userName;//userName.

    this._socket = null;//socket.
    this._mediaSoupDevice = null;//mediaSoup-client Device instance.
    this._sendTransport = null;//mediaSoup Transport for sending.
    this._recvTransport = null;//mediaSoup Transport for receiving.
    this._micProducer = null;//Local mic mediaSoup Producer.
    this._webcamProducer = null;//Local webcam mediaSoup Producer.
    this._shareProducer = null;//Local share mediaSoup Producer.
    this._consumers = new Map();//mediaSoup Consumers.
    this._consumersLength = 0;//mediaSoup Consumers.
  }
  /*客户端关闭*/
  close(reason){
    this._closed = true;
    if (this._socket&&this._socket.connected){
      this._socket.disconnect();//手动断开套接字
    }
    if (this._sendTransport){
      this._sendTransport.close();// Close mediasoup Transports.
    }
    if (this._recvTransport){
      this._recvTransport.close();// Close mediasoup Transports.
    }
    this.emit("close",reason);
  }
  /*加入房间*/
  joinRoom(){
    let _this=this;
    const {_roomId,_userName}=_this;
    const socket = io({
      path: '/server',
      auth: { token: "MEETING_CONNECT" },
      query: { "roomId":_roomId,"userName":_userName }
    });
    /*连接成功*/
    socket.on("connect", () => {
      _this.initRoom(socket);
    });
    /*断开连接时触发*/
    socket.on("disconnect", (reason) => {
      _this.close(reason);
    });
    /*命名空间中间件错误时触发*/
    socket.on("connect_error", err => {
      console.log(err); // true
      console.log(err.message); // not authorized
      _this.close(err.message);
    });
    // 接收到文本消息
    socket.on("onTextMessage", data => {
      _this.emit('onMessage',data);
    });
    _this.otherSocketEvent(socket);
    _this._socket=socket;
  }
  /*初始化房间*/
  async initRoom(socket) {
    try {
      let _this = this;
      _this._mediaSoupDevice = new mediaSoupClient.Device();
      const {routerRtpCapabilities} = await socketPromise(socket,'getServerRouterRtpCapabilities');//请求服务端路由rtp参数
      _this._mediaSoupDevice.load({ routerRtpCapabilities });//加载参数
      {
        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
        const audioTrack = stream.getAudioTracks()[0];
        audioTrack.enabled = false;
        setTimeout(() => audioTrack.stop(), 120000);
      }//提前获取麦克风权限
      {
        const {transportParams} = await socketPromise(socket,'createServerWebRtcTransport',{
          producing: true,//是否为生产Transport
          consuming: false//是否为消费Transport
        });//请求服务端创建生产Transport
        _this._sendTransport = _this._mediaSoupDevice.createSendTransport(transportParams);//创建客户端生产Transport
        /*connect事件：当传输即将建立ICE+DTLS连接并需要与关联的服务器端传输交换信息时发出*/
        _this._sendTransport.on('connect', async ({ dtlsParameters }, callback, errback) => {
          try{
            console.log('客户端生产Transport==需要关联==>服务端生产Transport',dtlsParameters)
            await socketPromise(socket,'connectServerWebRtcTransport',{ transportId : _this._sendTransport.id, dtlsParameters });
            callback();//告诉transport参数已传输成功
          }catch (error) {
            console.error(error);
            errback(error);//告诉transport参数传输失败
          }
        });
        /*produce事件：transport.produce({ track })调用之前会触发*/
        _this._sendTransport.on('produce', async ({ kind, rtpParameters,appData }, callback, errback) => {
          try {
            console.log('开始生产之前==请求远端创建远端生产者==》',kind,rtpParameters,appData)
            /*请求远端创建远端生产者*/
            const { producerId } = await socketPromise(socket,'produce', { transportId : _this._sendTransport.id,kind,rtpParameters,appData });
            callback({ id:producerId });//告诉传输参数已传输并提供服务器端生产者的ID
          } catch (err) {
            errback(err);
          }
        });
        _this._sendTransport.on('connectionstatechange', (connectionState) => {
          switch (connectionState) {
            case 'connecting':
              console.log("_sendTransport连接=====connecting")
              break;
            case 'connected':
              console.log("_sendTransport连接=====connected")
              break;
            case 'failed':
              console.log("_sendTransport连接=====failed")
              _this._sendTransport.close();
              break;
            default: break;
          }
        });
      }//创建客户端生产Transport
      {
        const {transportParams} = await socketPromise(socket,'createServerWebRtcTransport',{
          producing: false,//是否为生产Transport
          consuming: true//是否为消费Transport
        });
        _this._recvTransport = _this._mediaSoupDevice.createRecvTransport(transportParams);
        _this._recvTransport.on('connect', async ({ dtlsParameters }, callback, errback) => {
          try {
            console.log('客户端消费Transport==需要关联==>服务端消费Transport',dtlsParameters)
            await socketPromise(socket,'connectServerWebRtcTransport', { transportId : _this._recvTransport.id,dtlsParameters })
            callback();//告诉transport参数已传输成功
          } catch (error) {
            console.error(error)
            errback(error);//告诉transport参数传输失败
          }
        });
        _this._recvTransport.on('connectionstatechange', async (state) => {
          switch (state) {
            case 'connecting':
              console.log('_recvTransport连接状态===connecting')
              break;
            case 'connected':
              console.log('_recvTransport连接状态===connected')
              break;
            case 'failed':
              console.log('transport连接状态===failed')
              _this._recvTransport.close();
              break;
            default:
              break;
          }
        });
      }//创建客户端消费Transport
      /*请求加入房间*/
      await socketPromise(socket,'joinRoom',{
        device: _this._device,
        rtpCapabilities : _this._mediaSoupDevice.rtpCapabilities,
      });
      await _this.enableMic();//音频
      // _this.enableWebcam();//视频
      this.emit('open');
    } catch (error) {
      console.error('加入房间失败',error)
      this.close(error.message);
    }
  }
  /*其他事件*/
  otherSocketEvent(socket){
    let _this=this;
    /*消费者关闭，远端生产者那边关闭触发*/
    socket.on("consumerClosed", ({consumerId}) => {
      try{
        const consumer = _this._consumers.get(consumerId);
        consumer.close();
        _this._consumers.delete(consumerId);
        _this._consumersLength = _this._consumers.size;
      }catch (e) {
        console.error(e)
      }
    });
    /*消费者暂停，远端生产者那边暂停触发*/
    socket.on("consumerPaused", ({consumerId}) => {
      try{
        const consumer = _this._consumers.get(consumerId);
        consumer.pause();
      }catch (e) {
        console.error(e)
      }
    });
    /*消费者恢复，远端生产者那边恢复触发*/
    socket.on("consumerResumed", ({consumerId}) => {
      try{
        const consumer = _this._consumers.get(consumerId);
        consumer.resume();
      }catch (e) {
        console.error(e)
      }
    });
    /*创建新的消费者,远端已经创建成功*/
    socket.on("newConsumer", async (data, callback) => {
      try {
        const { producerId,id,kind,rtpParameters,type,appData,producerPaused } = data;
        console.log(`recvTransport创建的consume：${kind}`);
        const consumer = await _this._recvTransport.consume({id,producerId,kind,rtpParameters,appData });
        _this._consumers.set(consumer.id, consumer);
        _this._consumersLength = _this._consumers.size;
        consumer.on('transportclose', () => { _this._consumers.delete(consumer.id);_this._consumersLength = _this._consumers.size; });
        callback(responseSuccess());
      }catch (error) {
        callback(responseError(error));
      }
    });
  }
  /*创建micProducer音频生产者*/
  async enableMic() {
    let _this = this;
    if (_this._micProducer){ return; }
    if (!_this._mediaSoupDevice.canProduce('audio')) {
      console.log('cannot produce audio');
      return;
    }
    let track;
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      track = stream.getAudioTracks()[0];
      console.log('sendTransport创建micProducer============》produce：audio');
      _this._micProducer = await _this._sendTransport.produce({
        track,
        codecOptions : {
          opusStereo : 1,
          opusDtx    : 1
        }
      });
      _this._micProducer.on('transportclose', () => { _this._micProducer = null; });
      _this._micProducer.on('trackended', () => { _this.disableMic(); });
    } catch (error) {
      console.log('Error enabling microphone:', error);
      if (track){ track.stop(); }
    }
  }
  /*创建_webcamProducer视频生产者*/
  async enableWebcam() {
    let _this=this;
    if (_this._webcamProducer){
      return;
    }else if (_this._shareProducer){
      await _this.disableShare();//禁用视频
    }
    if (!_this._mediaSoupDevice.canProduce('video')) {
      console.error('cannot produce video');
      return;
    }
    let track;
    let device;
    try {
      device = await _this.getWebcamDevice();
      if (!device){
        throw new Error('no webcam devices');
      }
      const VIDEO_CONSTRAINS = {
        qvga : { width: { ideal: 320 }, height: { ideal: 240 } },
        vga  : { width: { ideal: 640 }, height: { ideal: 480 } },
        hd   : { width: { ideal: 1280 }, height: { ideal: 720 } }
      };
      const stream = await navigator.mediaDevices.getUserMedia({
        video : {
          deviceId : { ideal: device.deviceId },
          ...VIDEO_CONSTRAINS['hd']
        }
      });
      track = stream.getVideoTracks()[0];
      const codecOptions = { videoGoogleStartBitrate : 1000 };
      /*指定视频编码*/
      /*
      let codec;
      if (_this._forceH264) {
        codec = _this._mediaSoupDevice.rtpCapabilities.codecs.find((c) => c.mimeType.toLowerCase() === 'video/h264');
        if (!codec) {
          throw new Error('desired H264 codec+configuration is not supported');
        }
      } else if (_this._forceVP9) {
        codec = _this._mediaSoupDevice.rtpCapabilities.codecs.find((c) => c.mimeType.toLowerCase() === 'video/vp9');
        if (!codec) {
          throw new Error('desired VP9 codec+configuration is not supported');
        }
      }*/
      console.log('sendTransport创建webcamProducer===========》produce：video');
      _this._webcamProducer = await _this._sendTransport.produce({track,codecOptions});
      _this._webcamProducer.on('transportclose', () => { _this._webcamProducer = null; });
      _this._webcamProducer.on('trackended', () => { _this.disableWebcam(); });
    } catch (error) {
      console.error('Error enabling webcam', error);
      this.emit('error',"摄像头获取失败！");
      if (track){
        track.stop();
      }
    }
  }
  /*启动屏幕共享*/
  async enableShare() {
    let _this=this;
    if (_this._shareProducer){
      return;
    }else if (_this._webcamProducer){
      await _this.disableWebcam();
    }
    if (!_this._mediaSoupDevice.canProduce('video')) {
      console.error('enableShare() | cannot produce video');
      return;
    }
    let track;
    try {
      const stream = await navigator.mediaDevices.getDisplayMedia({
          audio : false,
          video : {
              displaySurface : 'monitor',
              logicalSurface : true,
              cursor         : true,
              width          : { max: 1920 },
              height         : { max: 1080 },
              frameRate      : { max: 30 }
            }
        });
      if (!stream) { return; }
      track = stream.getVideoTracks()[0];
      const codecOptions = { videoGoogleStartBitrate : 1000 };
      _this._shareProducer = await _this._sendTransport.produce({
          track,
          codecOptions,
          appData :{
            share : true
          }
        });
      _this._shareProducer.on('transportclose', () => { _this._shareProducer = null; });
      _this._shareProducer.on('trackended', () => { _this.disableShare(); });
    }catch (error) {
      console.error(error);
      if (track){track.stop();}
    }
  }
  /*禁止麦克风*/
  async disableMic() {
    if (!this._micProducer){return;}
    this._micProducer.close();
    try {
      await socketPromise(this._socket,'closeProducer', { producerId: this._micProducer.id });
    } catch (error) {
      console.error(error.message)
    }
    this._micProducer = null;
  }
  /*禁止屏幕共享*/
  async disableShare() {
    let _this=this;
    if (!_this._shareProducer){ return; }
    _this._shareProducer.close();
    try {
      await socketPromise(_this._socket,'closeProducer', { producerId: _this._shareProducer.id });
    } catch (error) {
      console.error('Error closing server-side share Producer',error)
    }
    _this._shareProducer = null;
  }
  /*禁止视频*/
  async disableWebcam() {
    let _this=this;
    if (!_this._webcamProducer){return;}
    _this._webcamProducer.close();
    try {
      await socketPromise(_this._socket,'closeProducer', { producerId: _this._webcamProducer.id });
    } catch (error) {
      console.error(error)
    }
    _this._webcamProducer = null;
  }
  /* 获取视频设备 */
  async getWebcamDevice() {
    const webcams = new Map();// Reset the list.
    const devices = await navigator.mediaDevices.enumerateDevices();
    for (const device of devices) {
      if (device.kind !== 'videoinput'){ continue; }
      webcams.set(device.deviceId, device);
    }
    const array = Array.from(webcams.values());
    return new Promise((resolve, reject)=>{
      if(array.length>0){
        resolve(array[0]);
      }
      reject();
    })
  }
  /*暂停麦克风*/
  async muteMic() {
    this._micProducer.pause();
    try {
      await socketPromise(this._socket, 'pauseProducer', {producerId: this._micProducer.id});
    } catch (e) {
      console.log(e)
    }
  }
  /*启动麦克风*/
  async unmuteMic() {
    this._micProducer.resume();
    try {
      await socketPromise(this._socket, 'resumeProducer', {producerId: this._micProducer.id});
    } catch (e) {
      console.log(e)
    }
  }
  /*发送文本消息*/
  async sendTextMessage(userName,message) {
    try {
      await socketPromise(this._socket, 'textMessage', {userName,message});
    } catch (e) {
      console.log(e)
    }
  }
}

/*给socketio创建返回Promise的一个发射事件方法*/
function socketPromise(socket,eventName, data = {}) {
  return new Promise((resolve,reject) => {
    socket.emit(eventName,data,(res)=>{
      if(res.status==200){
        resolve(res.data);
      } else {
        reject(res.error);
      }
    });//socket.emit(eventName [, ...args][, ack]);//args传参，sck服务端回调函数
  });
};
/*（工具类）socketio响应成功*/
function responseSuccess(data) {
  return {
    status:200,//成功状态
    data
  }
}
/*（工具类）socketio响应失败*/
function responseError(error) {
  return {
    status:1,//失败状态
    error
  }
}

export default RoomClient;
